home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / hardware / dat / dat.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  49KB  |  1,886 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*****************************************************************************
  18.  *    dat.c:    A simple DAT Tape Driver
  19.  *
  20.  *    Introduction:
  21.  *    ------------- 
  22.  *    This is a User-Level DAT device driver for Indigo-2.
  23.  *    The purpose of this driver is to provide examples as how to
  24.  *    use 'dslib.a' routines to drive any Scsci device.
  25.  *    The code in this file shows you how to:
  26.  *
  27.  *        - Load and Unload the tape
  28.  *        - find out the tape format (single or double part).
  29.  *        - find out tape capacity.
  30.  *        - partition a tape  
  31.  *        - switch partitions on the tape
  32.  *        - change default tape block
  33.  *        - Read and Write from/to the tape
  34.  *        - move forward and backward
  35.  *         ... and many more
  36.  *
  37.  *    All above functions are provided using 'dslib' routines.
  38.  *    To provide these examples, we had to come up with a DAT tape
  39.  *    format and Directory structure. We use the tape in Double
  40.  *    Partition mode: Partition 1 (the first physical partition of 
  41.  *    the tape) is used for the Tape Control block and Directory entries.
  42.  *    Partition 0 (the rest of the tape) is used for Data.
  43.  *
  44.  *    Partition 1: Control Block and Directoriy:
  45.  *    ------------------------------------------ 
  46.  *    Tape COntrol Block is used to keep track of data about the whole
  47.  *    tape in general (see tpCb_t). This data is written in Block 0 of 
  48.  *    the Partition 0. The rest of the partition (block 1 onward) is
  49.  *    used to store the directory entries. Currently, we use only one
  50.  *    block for the directory for simplicity's sake. You can easily change
  51.  *    this restriction. So, the format of partition one is:
  52.  *
  53.  *        +----------------+
  54.  *        |  Control Block |  block 0
  55.  *        +----------------+
  56.  *        |  Directory     |  block 1
  57.  *         +----------------+
  58.  *        |  Unused        |  block 2
  59.  *        ..................    ... 
  60.  *        |  Unused        |  block N
  61.  *        +----------------+
  62.  * 
  63.  *      Partition 0:  Data files:
  64.  *    -------------------------
  65.  *    The files themselves are stored in Partition 0. For each 
  66.  *    Directory entry in Partition 1, there is a corresponding file 
  67.  *    in Partition 0. The files are separated from each other by a 
  68.  *    File Mark after their last block. We use these File_Marks to
  69.  *    move around in the tape and access a particular file.
  70.  *    The format of Partition 0 is:
  71.  *
  72.  *        +-------------------+
  73.  *        |    File 0         |  block 0
  74.  *        +-------------------+
  75.  *         |    File 0         |  block 1
  76.  *        .....................    ...
  77.  *        |    File 0         |  block n
  78.  *        +===================+  <--------- File Mark
  79.  *            ....
  80.  *        +===================+  
  81.  *        |    File N         |  
  82.  *        +-------------------+
  83.  *        |    File N         |  
  84.  *         .....................
  85.  *        |    File N         |
  86.  *        +===================+  <--------- File Mark
  87.  *
  88.  *      Technical References:
  89.  *    ---------------------
  90.  *    Python DDS and DDS-DC Dat Tape Drives, Scsi Manual
  91.  *    IRIX Device Driver Programmer's Guide, Scsi User-Level Device Drivers.
  92.  *
  93.  *****************************************************************************/
  94. #include <sys/types.h>
  95. #include <stdio.h>
  96. #include <string.h>
  97. #include <fcntl.h>
  98. #include <errno.h> 
  99. #include <memory.h>
  100. #include <sys/stat.h>
  101. #include <time.h>
  102. #include <dslib.h>
  103.  
  104.  
  105. /*
  106.  *    tpCb_t:   Tape Control Block
  107.  *
  108.  *    This struct contains data regarding the whole tape and 
  109.  *    is stored in Partition 1, block 0. We keep track of the 
  110.  *    following data in this struct:
  111.  *
  112.  *        - cb_dtot:     Total directory entries in this tape.
  113.  *        - cb_time:     The last Date/time this tape was written to.
  114.  *        - cb_remsize:  Remaining tape capacity in K Bytes.
  115.  *        - cb_maxsize:  Maximum tape capacity in K Bytes.
  116.  *        - cb_id:       Tape ID to recognize the tape as SGI tape.
  117.  *        - cb_name:     Tape name.
  118.  */ 
  119. typedef struct tpCb_s {
  120.     int    cb_dtot;    /*  total Directory entries  */
  121.     long    cb_time;    /*  last date/time tape was written to */
  122.     int     cb_remsize; /*  remainig K Bytes on tape (part-0) */
  123.     int     cb_maxsize; /*  Max size of the tape (part 0, K Bytes) */
  124.     uchar_t cb_id[80];  /*  tape ID - always set to SGI_TAPE */
  125.     uchar_t cb_name[80];/*  tape Name/Description    */
  126. } tpCb_t;
  127.  
  128.  
  129. /*   cb_id  value   */
  130. #define SGI_TAPE       "SGI TAPE VERSION 1.0"
  131.  
  132.  
  133.  
  134. /*      ================================
  135.  *    tpDir_t:    Tape Directory Entry
  136.  *    ================================
  137.  *
  138.  *    There is one Directory entry for each file on tape.
  139.  *    The directory of the tape is stored in Partition 1, Block 1
  140.  *    afterwards. The data we keep for each file is:
  141.  *
  142.  *        - d_fno:       File number 
  143.  *        - d_len:       File Length in bytes.
  144.  *        - d_time:      Creation date of the file on tape.
  145.  *        - d_type:      File type (always TYPE_FILE for now).
  146.  *        - d_name:      File name 
  147.  */
  148. #define TP_NAME_LEN   25
  149.  
  150. typedef struct tpDir_s {
  151.     int    d_fno;            /*  file Number          */
  152.     int    d_len;            /*  file length (bytes)  */
  153.     long    d_time;            /*  date/time of creation */
  154.     uchar_t    d_type;            /*  file type            */
  155.     char    d_name[TP_NAME_LEN];    /*  file name            */
  156. } tpDir_t;
  157.  
  158. /*    d_type values - can be added for custom files  */
  159. #define  TYPE_FILE   1
  160.  
  161.  
  162.  
  163. /*    =========================
  164.  *    tpStat_t:     Tape Status
  165.  *    =========================
  166.  *
  167.  *    This struct is used to keep track of current running info.
  168.  *    This is run-time based and is never written to the tape.
  169.  *    The data we keep track of is as follow:
  170.  *
  171.  *        - st_state:   State of the tape 
  172.  *        - st_fno:     Current Tape position (which file).
  173.  *        - st_part:    Current Tape Partition (DIR or DATA)
  174.  *        - st_dno:     Last Directory entry shown    
  175.  */ 
  176. typedef struct tpStat_s {
  177.     int    st_state;    /*  i.e: tape opened, written to, etc   */
  178.     int    st_fno;        /*  current file position (file number) */
  179.     int    st_part;        /*  current Partition on tape           */
  180.     int    st_dno;         /*  Last directory shown  */
  181. } tpStat_t;
  182.  
  183. /*   bits in st_state    */
  184. #define  TAPE_LOADED            0x01
  185. #define  TAPE_WRITTEN           0x02
  186. #define  TAPE_WRITE_PROTECT     0x04
  187. #define  TAPE_DOUBLE_PART       0x08
  188.  
  189. /*   st_part  values   */
  190. #define  DIR_PARTITION     1
  191. #define  DATA_PARTITION    0
  192.     
  193.  
  194.  
  195.  
  196. /*   The program commands supported  */
  197. #define  CMD_HELP       "help"
  198. #define  CMD_INIT       "init"
  199. #define  CMD_OPEN       "open"
  200. #define  CMD_DIR        "dir"
  201. #define  CMD_DIRU       "diru"
  202. #define  CMD_DIRD       "dird"
  203. #define  CMD_WRITE      "write"
  204. #define  CMD_READ       "read"
  205. #define  CMD_CLOSE      "close"
  206. #define  CMD_EXIT       "exit"
  207.  
  208. /*  Scsi tape commands   */
  209. #define  WRITE_FM       0x10
  210. #define  WRITE        0x0a
  211. #define  READ        0x08
  212. #define  REWIND        0x01
  213. #define  LOAD_UNLOAD    0x1B
  214. #define  SPACE          0x11
  215. #define  MODE_SENSE     0x1A
  216. #define  MODE_SELECT    0x15
  217. #define  LOG_SENSE      0x4D
  218.  
  219. /*   Scsi commands Pages and various bits  */
  220. #define  MED_PART_PAGE       0x11
  221. #define  DEVICE_CONFIG_PAGE  0x10
  222. #define  TAPE_CAPACITY_PAGE  0x31
  223. #define  SPACE_FMARKS        0x01
  224. #define  DBD_BIT             0x08
  225. #define  IDP_BIT             0x20
  226. #define  PC_BIT              0x40
  227. #define  PF_BIT              0x10
  228. #define  CAP_BIT             0x40
  229. #define  LOAD_BIT         0x01
  230. #define  RSMK_BIT            0x20;
  231. #define  UNLOAD_BIT         0x00
  232. #define  PSUM_MBYTES         0x10
  233. #define  PSUM_KBYTES         0x08
  234. #define  PSUM_BYTES          0x00
  235.  
  236. /*   Misc. Defines   */
  237. #define  TAPE_BLOCK_SIZE     1024
  238. #define  MAX_DIR             (TAPE_BLOCK_SIZE / sizeof(tpDir_t) )
  239. #define  MAX_BUSY_RETRY      3
  240. #define  FORWARD             1
  241. #define  BACKWARD            0
  242. #define  FOREVER             for(;;)
  243. #define  DIR_PARTITION_SIZE  1    /*  1 Mbytes for Directory partition */
  244. #define  SCREEN_SIZE         30   /*  Screen size for showing dir */
  245.  
  246. /*
  247.  *    some SCSI commands takes longer to be executed.
  248.  *    These are time out values in seconds for those commands.
  249.  *    ( 300 = 5 Minutes, 600 = 10 minutes )
  250.  */
  251. #define  TIMER_READ_WRITE       120*1000     /*  1 minutes   */
  252. #define  TIMER_LOAD_UNLOAD      300*1000     /*  5 minutes   */
  253. #define  TIMER_SWITCH_PARTITION 300*1000     /*  5 minutes   */
  254. #define  TIMER_REWIND           300*1000     /*  5 minutes   */
  255. #define  TIMER_FILE_MARKS       300*1000     /*  5 minutes   */
  256. #define  TIMER_DOUBLE_PARTIION  900*1000     /* 15 minutes   */
  257.  
  258. #define  seq(s1,s2)   (strcmp(s1,s2) == 0)
  259.  
  260.  
  261. /*    Global variables  */ 
  262. tpStat_t     tpStat;
  263. dsreq_t      *tpDsreq;
  264. uchar_t      tpBlock[TAPE_BLOCK_SIZE];
  265. char         *tpdev;
  266. tpCb_t       tpCb;
  267. tpDir_t      tpDir[MAX_DIR];
  268.  
  269.  
  270.  
  271. /****************************************************************************
  272.  *                                            *
  273.  *               M a i n     P r o g r a m                   *
  274.  *                                            *
  275.  ****************************************************************************/
  276. main (int argc, char **argv)
  277. {
  278.  
  279.     register int flags;
  280.     char         tpCmd[80];
  281.  
  282.     if ( argc <= 1 ) {
  283.         printf ("dat Usage:  dat </dev/<scsi DAT tape>\n");
  284.         exit();
  285.     }
  286.  
  287.     tpdev = argv[1];
  288.  
  289.     if ( (tpDsreq = dsopen(tpdev, O_RDWR)) == (dsreq_t *)NULL ) {
  290.         printf ("dat: Error opening DAT tape, errno = %d\n", errno );
  291.         exit();
  292.     }
  293.  
  294.     /*  dsdebug |= (1 | 2);  */
  295.  
  296.     memset ( &tpStat, 0, sizeof(tpStat_t) );
  297.  
  298.  
  299.     FOREVER {
  300.         printf ("\n\ndat> ");
  301.         printf ("dat> Commands: init, open, close, read, write, exit\n");
  302.         printf ("dat>           dir, diru (Dir Up), dird (Dir down)\n");
  303.         printf ("\ndat> ");
  304.         gets (tpCmd);
  305.         
  306.     
  307.         /*   Help command  */
  308.         if ( seq(tpCmd, CMD_HELP) ) {
  309.             showHelp();
  310.             continue;
  311.         }
  312.         /*   Initialize command  */
  313.         if ( seq(tpCmd, CMD_INIT) ) {
  314.             initTape();
  315.             continue;
  316.         }
  317.  
  318.         /*   Open Command        */
  319.         if ( seq(tpCmd, CMD_OPEN) ) {
  320.             openTape();
  321.             continue;
  322.         }
  323.  
  324.         /*    Show Directory    */
  325.         if ( seq(tpCmd, CMD_DIR) ) {
  326.             showDir(0);
  327.             continue;
  328.         }
  329.  
  330.                 /*    Directory Up    */
  331.                 if ( seq(tpCmd, CMD_DIRU) ) {
  332.                         showDir(tpStat.st_dno - 2 * SCREEN_SIZE);
  333.                         continue;
  334.                 }
  335.  
  336.                 /*    Directory Down    */
  337.                 if ( seq(tpCmd, CMD_DIRD) ) {
  338.                         showDir(tpStat.st_dno);
  339.                         continue;
  340.                 }
  341.  
  342.         /*    Write Command    */ 
  343.         if ( seq(tpCmd, CMD_WRITE) ) {
  344.             writeFile();
  345.             continue;
  346.         }
  347.  
  348.         /*    Read Command     */
  349.         if ( seq(tpCmd, CMD_READ) ) {
  350.             readFile();
  351.             continue;
  352.         }
  353.  
  354.         /*    Close Tape        */
  355.         if ( seq(tpCmd, CMD_CLOSE) ) {
  356.             closeTape();
  357.             continue;
  358.         }
  359.  
  360.         if ( seq(tpCmd, CMD_EXIT) ) {
  361.             cleanUp();
  362.         }
  363.  
  364.         printf ("Unknown command ..try help.");
  365.     }
  366.  
  367. }
  368.         
  369.     
  370.     
  371.  
  372. /*************************************************************************
  373.  *            i n i t T a p e               
  374.  *************************************************************************
  375.  *
  376.  *  Name:       initTape
  377.  *    
  378.  *  Purpose:    Initializes a tape.
  379.  *
  380.  *  Returns:    0 = Success, -1 = Failure.
  381.  *
  382.  *************************************************************************/
  383.  
  384. initTape()
  385. {
  386.     register tpCb_t   *pcb;
  387.     tpCb_t            tpcb;
  388.     int          part_size, max_size, rem_size;
  389.  
  390.     if ( tpStat.st_state & TAPE_WRITE_PROTECT ) {
  391.         printf ("dat> Tape is Write Protected ..cannot Initialize\n");
  392.         return(-1);
  393.     }
  394.  
  395.     pcb = &tpcb;
  396.  
  397.     memset ( pcb, 0, sizeof(tpCb_t) );
  398.     printf ("dat> Enter tape name below <Return> to exit:\n");
  399.     printf ("dat> Name: ");
  400.     gets ( pcb->cb_name );
  401.  
  402.     if ( ( pcb->cb_name[0] == '\n') || ( pcb->cb_name[0] == '\0') )
  403.         return(0);
  404.  
  405.     sprintf ( pcb->cb_id, "%s", SGI_TAPE);
  406.  
  407.      /*
  408.      *    Tape is not loaded yet. 
  409.      */
  410.     if ( !(tpStat.st_state & TAPE_LOADED) ) {
  411.  
  412.         /*   Do a Test Unit Ready first  */
  413.             if ( testUnitReady() == -1 ) {
  414.                     printf ("dat> Tape is not ready !\n");
  415.                     return(-1);
  416.             }
  417.  
  418.         if ( getTapeFormat(&part_size) == -1 ) 
  419.             return(-1);
  420.  
  421.         /*
  422.          *   If tape is not partitioned or it is partitioned but
  423.          *   Dir partition is more than our specs, repartition it.
  424.          */
  425.         if (  part_size != DIR_PARTITION_SIZE ) { 
  426.             if ( initDoublePart() == -1 ) 
  427.                 return(-1);
  428.         }
  429.  
  430.         /*  tape is already double_partitioned, just load it */ 
  431.         else {
  432.             if ( loadTape() == -1 ) 
  433.             return(-1);
  434.         }
  435.     }
  436.  
  437.     if ( switchPart(DIR_PARTITION) == -1 ) 
  438.         return(-1);
  439.  
  440.  
  441.     /*   get tape capacity   */
  442.     if ( getTapeSize(&max_size, &rem_size) == -1 ) 
  443.         return(-1);
  444.  
  445.     pcb->cb_remsize = pcb->cb_maxsize = max_size;
  446.  
  447.     /*   so far so good ..initialize tape Control Block  */
  448.     printf ("dat>\t ...initializing tape Control Block.\n");
  449.     memset (tpBlock, 0, TAPE_BLOCK_SIZE);
  450.     if ( time ( &pcb->cb_time ) == -1 )
  451.         printf ("dat>  Couldn't get date/time ..errno = %d\n", errno);
  452.  
  453.     memcpy ( &tpCb, pcb, sizeof(tpCb_t) );
  454.     memcpy ( tpBlock, pcb, sizeof(tpCb_t) );
  455.  
  456.     if ( writeBlock() == -1 ) 
  457.         return(-1);
  458.  
  459.     /*   Initialize tape Directories   */
  460.     memset (tpBlock, 0, TAPE_BLOCK_SIZE);
  461.     printf ("dat>\t ...initializing Tape Directory.\n");
  462.         if ( writeBlock() == -1 )
  463.                 return(-1);
  464.  
  465.     memset ( &tpDir, 0, sizeof(tpDir) );
  466.     if ( switchPart(DATA_PARTITION) == -1 ) 
  467.         return(-1);
  468.  
  469.     tpStat.st_state |= TAPE_LOADED;
  470.     tpStat.st_state &= ~TAPE_WRITTEN;
  471.     tpStat.st_fno  = 0; 
  472.  
  473.     printf ("dat>\t ...Tape is initialized\n");
  474.     return(0);    
  475.     
  476. }
  477.  
  478.  
  479. /*************************************************************************
  480.  *            o p e n T a p e               
  481.  *************************************************************************
  482.  *
  483.  *  Name:       openTape
  484.  *    
  485.  *  Purpose:    Opens the tape, reads in tape control block and 
  486.  *        Directories. If everything goes well, we switch to
  487.  *        Data partition (partition 0).
  488.  *
  489.  *  Returns:    0 = Success, -1 = Error
  490.  *
  491.  *************************************************************************/
  492. openTape()
  493. {
  494.     register tpCb_t    *tpcb;
  495.     register tpStat_t  *tpst;
  496.     int           part_size;
  497.  
  498.     if ( tpStat.st_state & TAPE_LOADED ) {
  499.         printf ("dat> Tape is already opened\n");
  500.         return(-1);
  501.     }
  502.  
  503.     tpst = &tpStat;
  504.     tpcb = &tpCb;
  505.     memset (tpst, 0, sizeof(tpStat_t) );
  506.  
  507.     if ( testUnitReady() == -1 ) {
  508.         printf ("dat> Tape is not ready !\n");
  509.         return(-1);
  510.     }
  511.  
  512.  
  513.     /*
  514.      *     first see if tape is partitioned correctly
  515.      */
  516.     if ( getTapeFormat(&part_size) == -1 ) 
  517.         return(-1);
  518.  
  519.     if ( part_size != DIR_PARTITION_SIZE ) {
  520.         printf ("dat> Foreign tape detected ..not an SGI tape\n");
  521.         unloadTape();
  522.         return(-1);
  523.     }
  524.  
  525.     /*   Load the tape   */
  526.     if ( loadTape() == -1 ) 
  527.         return(-1); 
  528.  
  529.         if ( switchPart(DIR_PARTITION) == -1 )
  530.                 return(-1);
  531.  
  532.     /*   We are in Dir Partition ..read tape Control Block   */
  533.     printf ("dat>\t ...reading Tape Control Block\n");
  534.     if ( readBlock() == -1 ) 
  535.         return(-1);
  536.  
  537.     memcpy ( tpcb, DATABUF(tpDsreq), sizeof(tpCb_t) );
  538.  
  539.         
  540.     if ( !seq(tpcb->cb_id, SGI_TAPE) ) {
  541.         printf ("dat>  This is not a SGI formatted tape\n");
  542.         unloadTape();
  543.         return(-1);
  544.     }
  545.  
  546.     printf ("dat>\t ...tape name = %s, total files = %d\n",
  547.         tpcb->cb_name, tpcb->cb_dtot);
  548.  
  549.     /*  Read tape Directory    */
  550.     if ( tpcb->cb_dtot > 0 ) {
  551.         printf ("dat>\t ...reading the tape directory\n");
  552.         if ( readBlock() == -1 ) 
  553.             return(-1);
  554.         memcpy ( &tpDir[0], DATABUF(tpDsreq), 
  555.              tpcb->cb_dtot * sizeof(tpDir_t) );
  556.     }
  557.  
  558.     if ( switchPart(DATA_PARTITION)    == -1 ) 
  559.         return(-1);
  560.  
  561.     tpst->st_fno = 0;
  562.  
  563.     printf ("dat>\t ...tape is ready\n");
  564.     return(0);
  565. }
  566.  
  567.  
  568. /*************************************************************************
  569.  *            w r i t e F i l e          
  570.  *************************************************************************
  571.  *
  572.  *  Name:       writeFile
  573.  *    
  574.  *  Purpose:    Writes a given file from Disk to Tape  
  575.  *
  576.  *  Returns:    0 = Success, -1 = Error
  577.  *
  578.  *************************************************************************/
  579. writeFile()
  580. {
  581.  
  582.     register int         i, totbytes, totblocks, bytes, fd;
  583.     register tpStat_t   *tps;
  584.     register tpCb_t     *tpc;
  585.     register tpDir_t    *tpd;
  586.     uchar_t             fn[80], fpath[80];
  587.     struct stat           st;
  588.  
  589.  
  590.     tps = &tpStat;
  591.     tpc = &tpCb;
  592.  
  593.  
  594.     /*   is there any tape at all ?   */
  595.     if ( !(tps->st_state & TAPE_LOADED) ) {
  596.         printf ("dat> No tape is opened yet\n");
  597.         return(-1);
  598.     }
  599.  
  600.     if ( tps->st_state & TAPE_WRITE_PROTECT ) {
  601.         printf ("dat> Tape is Write Protected\n");
  602.         return(-1);
  603.     }
  604.  
  605.     /*  Get the file path and name    */ 
  606.     printf ("dat>  Below enter the full path name to the file:\n");
  607.     printf ("dat>  File: ");
  608.     gets (fpath);  
  609.  
  610.     if ( (fpath[0] == '\n') || (fpath[0] == '\n') ) 
  611.         return(0);
  612.  
  613.     /*  see if this file is just a plain ordinary file   */
  614.     if ( stat(fpath, &st) == -1 ) {
  615.         printf ("dat> Cannot get stat of the file, errno = %d\n", 
  616.             errno);     
  617.         return(-1);
  618.     }
  619.  
  620.     /*   is it a regular file ? (see sys/stat.h)  */
  621.     if ( !(S_ISREG(st.st_mode)) ) {
  622.         printf ("dat> This is not a regular file\n");
  623.         return(-1);
  624.     }
  625.  
  626.     /*    calculate total tape blocks needed  */
  627.     totblocks = 1;
  628.     totbytes  = st.st_size;
  629.     if ( totbytes > TAPE_BLOCK_SIZE ) {
  630.         totblocks = totbytes / TAPE_BLOCK_SIZE;
  631.         if ( totbytes % TAPE_BLOCK_SIZE > 0 ) 
  632.             totblocks++;
  633.     }
  634.  
  635.     /*  do we have enough room ?   */
  636.     if ( tpc->cb_remsize <= totblocks ) {
  637.         printf ("dat> Not enough room on tape ..rem = %d, needed = %d\n",
  638.             tpc->cb_remsize, totblocks );
  639.         return(-1);
  640.     }
  641.  
  642.     printf ("dat>\t ...Writing the file as File No: %d\n", tpc->cb_dtot);
  643.     printf ("dat>\t ...currently at file no: %d\n", tps->st_fno );
  644.  
  645.     /*
  646.      *  We are not at the end of the tape ..move to the end of tape.
  647.       *  Note: Since each file's last position is marked with a 
  648.      *  File Mark, we move <currnt_file - total_file> File Mark
  649.      *  forward in tape. The tape position will be at the end of 
  650.      *  the last File Mark detected, which places the tape position
  651.      *  on the beginning of the block we want to write.
  652.      */
  653.     if ( tps->st_fno != tpc->cb_dtot ) {
  654.         printf ("dat> Moving to End of Tape\n"); 
  655.         if ( moveFm ( FORWARD, tpc->cb_dtot - tps->st_fno) == -1 )
  656.             return(-1);
  657.     } 
  658.             
  659.     /*   We have absolutly no excuse now ! Get the file name  */ 
  660.     for ( i = strlen(fpath) - 1; i>= 0; i-- ) {
  661.  
  662.         if ( fpath[i] == '/' ) {
  663.             sprintf (fn, &fpath[i+1]);
  664.             break;
  665.         }
  666.  
  667.         if ( i == 0 ) {
  668.             sprintf (fn, fpath);
  669.             break;
  670.         }
  671.                 
  672.     }
  673.  
  674.     /*   open the file name    */
  675.     if ( (fd = open(fpath, O_RDONLY|O_NONBLOCK)) <= 0  ) {
  676.         printf ("dat> Error opening file, errno = %d\n", errno);
  677.         return(-1);
  678.     }
  679.  
  680.     /*
  681.      *    Read and write to the tape
  682.      */
  683.  
  684.     printf ("dat>\t ...Writing %d bytes (total of %d tape blocks)\n", 
  685.         totbytes, totblocks );
  686.  
  687.     while ( totbytes > 0 ) {
  688.         bytes = (totbytes > TAPE_BLOCK_SIZE ? TAPE_BLOCK_SIZE:totbytes);
  689.  
  690.         /*  if reading less than block size, zero out the block */
  691.         if ( bytes < TAPE_BLOCK_SIZE )  
  692.             memset (tpBlock, 0, TAPE_BLOCK_SIZE); 
  693.  
  694.         if ( (bytes = read (fd, tpBlock, bytes)) < 0 ) {
  695.             printf ("dat> Read error, errno = %d\n", errno);
  696.             return(-1);
  697.         }
  698.  
  699.         totbytes -= bytes;
  700.  
  701.         if ( writeBlock() == -1 ) {
  702.             printf ("dat> Error writing to tape\n");
  703.             return(-1);
  704.         }
  705.     }
  706.  
  707.     close (fd);
  708.  
  709.     if ( writeFm() == -1 ) {
  710.         printf ("dat> Could not write End_of_File Mark\n");
  711.         return(-1);
  712.     }
  713.  
  714.     tpc->cb_remsize -= totblocks;
  715.  
  716.     /*   create a directory entry for this file  */
  717.     tpd         = &tpDir[tpc->cb_dtot];
  718.     sprintf (tpd->d_name, fn);
  719.     tpd->d_fno  = tpc->cb_dtot++;
  720.     tpd->d_len  = st.st_size; 
  721.     tpd->d_type = TYPE_FILE;
  722.     time( &tpd->d_time );
  723.  
  724.         printf ("dat>\t ...File written as File No: %d\n", tpd->d_fno ); 
  725.         printf ("dat>\t ...Total files on tape: %d, currently at file no: %d\n",
  726.          tpc->cb_dtot, tps->st_fno );
  727.  
  728.     return(0);
  729.  
  730. }
  731.  
  732. /*************************************************************************
  733.  *                      r e a d F i l e
  734.  *************************************************************************
  735.  *
  736.  *  Name:       readFile
  737.  *
  738.  *  Purpose:    Read a file from tape to disk.        
  739.  *
  740.  *  Returns:    0 = Success, -1 = Error
  741.  *
  742.  *************************************************************************/
  743. readFile()
  744. {
  745.  
  746.         register int         i, totbytes, totblocks, bytes;
  747.         register tpStat_t   *tps;
  748.         register tpCb_t     *tpc;
  749.         register tpDir_t    *tpd;
  750.     register int         fd;
  751.     int             fno;
  752.         uchar_t             fpath[80];
  753.  
  754.  
  755.         tps = &tpStat;
  756.     tpc = &tpCb;
  757.  
  758.         /*   is there any tape at all ?   */
  759.         if ( !(tps->st_state & TAPE_LOADED ) ) {
  760.                 printf ("dat> No tape is opened yet\n");
  761.                 return(-1);
  762.         }
  763.  
  764.     /*  Get the file number on tape  */
  765.     FOREVER {
  766.         printf ("dat> File number to copy <-1 = exit>: ");
  767.         scanf ("%i", &fno);
  768.  
  769.         if ( fno == -1 ) 
  770.             return(0);
  771.  
  772.         if ( fno >= tpc->cb_dtot ) {
  773.             printf ("dat> No such file number ..try again\n");
  774.             continue;
  775.         }
  776.  
  777.         break;
  778.     }
  779.  
  780.     gets(fpath);  /*  get rid of CR left over */
  781.  
  782.     /*
  783.      *    show info for this file and get the path to the
  784.       *    disk file name to create.
  785.      */
  786.     tpd = &tpDir[fno];
  787.     printf ("dat>\t  ...File no: %d,  Name: %s,  Size: %d\n",
  788.         tpd->d_fno, tpd->d_name, tpd->d_len );
  789.         printf ("dat> Below enter the full path name to store the file\n");
  790.         printf ("dat> Name: ");
  791.         gets (fpath);
  792.         if ( (fpath[0] == '\n') || (fpath[0] == '\n') )
  793.                 return(0);
  794.  
  795.     /*   Open the file - the old content will be lost */
  796.     if ( (fd = creat(fpath, S_IREAD | S_IWRITE)) < 0 ) {
  797.         printf ("dat> Cannot create/open file, errno = %d\n", errno);
  798.         return(-1);
  799.     }
  800.          
  801.  
  802.     
  803.     printf ("dat>\t ...current file position: %d, requested file: %d\n",
  804.         tps->st_fno, fno );
  805.  
  806.     /*
  807.      *    if requested file is 0, simply rewind the tape
  808.      */
  809.     if ( fno == 0 ) {
  810.                if ( rewindTape() == -1 )
  811.                       return(-1);
  812.     }
  813.  
  814.     /*
  815.      *    File is other that file 0 and we are at the requested file.
  816.      *    We may be in middle or the last block of the file.
  817.      *    We should get back to block 0 of the file. So, we should
  818.      *    skip one File_Mark backward and then one File_Marl Forward.
  819.      */
  820.     else { 
  821.         if ( fno == tps->st_fno ) {
  822.         if ( moveFm(BACKWARD, 1) == -1 )
  823.             return(-1);
  824.  
  825.         if ( moveFm(FORWARD, 1) == -1 )
  826.             return(-1);
  827.          }
  828.  
  829.         /*   we are not at the requested file number  */ 
  830.         else { 
  831.            /*   
  832.          *    Move the tape to the requested file.
  833.          *    Depending on where we are in relation to the file, we 
  834.          *    move File_Marks forward or backward. When moving backward,
  835.          *    we have to move Forward one File_Mark to be at the block
  836.          *    0 of the file.
  837.          */ 
  838.         if ( tps->st_fno < fno ) {
  839.             if ( moveFm (FORWARD, fno - tps->st_fno ) == -1 )
  840.                 return(-1);
  841.         }
  842.         else {
  843.                 if ( moveFm (BACKWARD, (tps->st_fno - fno)+1 ) == -1 )
  844.             return(-1);
  845.  
  846.             if ( moveFm (FORWARD, 1) == -1 ) 
  847.                 return(-1);
  848.         }
  849.         }
  850.  
  851.     }
  852.  
  853.     totbytes = tpd->d_len;
  854.     
  855.     /*   calculate tape blocks to read.  */
  856.     totblocks = 1;
  857.     if ( totbytes > TAPE_BLOCK_SIZE ) {
  858.         totblocks = totbytes / TAPE_BLOCK_SIZE;
  859.         if ( totbytes % TAPE_BLOCK_SIZE > 0 ) 
  860.             totblocks++;
  861.     }
  862.  
  863.     printf ("dat>\t ...Reading total of %d bytes [ %d blocks ]\n", 
  864.         totbytes, totblocks );
  865.  
  866.     while ( totblocks > 0 ) {
  867.         if ( readBlock() == -1 ) {
  868.             printf ("dat> Error reading from tape\n");
  869.             close (fd);
  870.             return(-1);
  871.         }
  872.  
  873.  
  874.         bytes = (totbytes > TAPE_BLOCK_SIZE ? TAPE_BLOCK_SIZE:totbytes);
  875.  
  876.         if ( (bytes = write (fd, tpBlock, bytes)) <= 0 ) {
  877.             printf ("dat> Error writing to file, errno = %d\n",
  878.                 errno );
  879.             close (fd);
  880.             return(-1);
  881.         }
  882.  
  883.         totbytes -= bytes;
  884.         totblocks--;
  885.     }
  886.     
  887.     close (fd);
  888.     printf ("dat>\t ...File successfully written to disk\n");
  889.  
  890.     return(0);
  891.  
  892. }
  893.     
  894.         
  895. /*************************************************************************
  896.  *            c l o s e T a p e               
  897.  *************************************************************************
  898.  *
  899.  *  Name:      closeTape
  900.  *    
  901.  *  Purpose:   Closes the tape. It updates the directory if needed.
  902.  *
  903.  *  Returns:   0 = Success, -1 = Error
  904.  *
  905.  *************************************************************************/
  906. closeTape()
  907. {
  908.     register tpStat_t    *tps;
  909.     register tpCb_t        *tpc;
  910.  
  911.     tps = &tpStat;
  912.     tpc = &tpCb;
  913.  
  914.  
  915.     /*   one or more files were written to tape ..update directory */
  916.     if ( tps->st_state & TAPE_WRITTEN ) {
  917.         printf ("dat>\t ...Updating Directory\n");
  918.  
  919.         if ( switchPart(DIR_PARTITION) == -1 ) 
  920.             return(-1);
  921.  
  922.         /*  write tape Control Block   */
  923.         printf ("dat>\t ...Writing Tape Control Block\n");
  924.         memset (tpBlock, 0, TAPE_BLOCK_SIZE );    
  925.         time (&tpc->cb_time);
  926.         memcpy (tpBlock, tpc, sizeof(tpCb_t) );
  927.  
  928.         if ( writeBlock() == -1 ) 
  929.             return(-1);
  930.  
  931.         /*  write tape Directory back to tape   */
  932.         printf ("dat>\t ...Writing Tape Directory\n");
  933.         memset (tpBlock, 0, TAPE_BLOCK_SIZE);
  934.         memcpy (tpBlock, &tpDir[0], tpc->cb_dtot * sizeof(tpDir_t) );
  935.  
  936.         if ( writeBlock() == -1 ) 
  937.             return(-1);
  938.     }
  939.     
  940.  
  941.     unloadTape();
  942.     tps->st_state &= ~TAPE_WRITTEN;
  943.     tps->st_state &= ~TAPE_LOADED;
  944.  
  945.     return(0);
  946. }
  947.  
  948.  
  949.  
  950. /*************************************************************************
  951.  *            m o v e F m                  
  952.  *************************************************************************
  953.  *
  954.  *  Name:      moveFm
  955.  *    
  956.  *  Purpose:   Moves given number of File Marks Forward or Backward.
  957.  *           The important thing to remember here is that when 
  958.  *           moving forward, tape will be positioned at the End of 
  959.  *           the last File_Mark (first block of the next file). When
  960.  *           moving backward, tape will be positioned at the Beginning
  961.  *           of the last File_Mark detected (the last block of the prev.
  962.  *           file).
  963.  *
  964.  *  Returns:   0 = Success, -1 = Error
  965.  *
  966.  *************************************************************************/
  967. moveFm (int what, int fmNo)
  968. {
  969.     uchar_t     b1, b2, b3, b4;
  970.     ulong        old_timer;
  971.     register int    err;
  972.  
  973.     printf ("dat>\t ...Moving the tape %d File_Marks %s, current pos: %d\n",
  974.         fmNo, (what == FORWARD ? "Forward":"Backward"), tpStat.st_fno); 
  975.  
  976.     fmNo = (what == FORWARD ? fmNo : fmNo * -1);
  977.  
  978.     /*   create a Space Cdb   */
  979.     b1 = 0;
  980.     b1 |= SPACE_FMARKS;
  981.     b2 = fmNo >> 16;
  982.     b3 = fmNo >> 8;
  983.     b4 = fmNo;
  984.  
  985.     fillg0cmd (tpDsreq, CMDBUF(tpDsreq), SPACE, b1, b2, b3, b4, 0 );
  986.         filldsreq (tpDsreq, 0, 0, DSRQ_READ | DSRQ_SENSE);      
  987.  
  988.         old_timer = TIME(tpDsreq);
  989.         TIME(tpDsreq) = TIMER_FILE_MARKS;
  990.         err = issueCmd();
  991.         TIME(tpDsreq) = old_timer;  /* put back the timer */
  992.  
  993.     if ( err == -1 ) {
  994.         printf ("dat>  Could not position tape\n");
  995.         return(-1);
  996.     }
  997.  
  998.     /*   update our position   */
  999.     tpStat.st_fno += fmNo;
  1000.     printf ("dat>\t ...new file position = %d\n", tpStat.st_fno);
  1001.     return(0);
  1002. }
  1003.  
  1004.     
  1005.  
  1006. /*************************************************************************
  1007.  *            t e s t U n i t R e a d y     
  1008.  *************************************************************************
  1009.  *
  1010.  *  Name:       testUnitReady
  1011.  *    
  1012.  *  Purpose:    Issues Test_Unit_Ready.  
  1013.  *
  1014.  *  Returns:    0 = Success (tape is ready), -1= Error
  1015.  *
  1016.  *************************************************************************/
  1017. testUnitReady()
  1018. {
  1019.     register int    i, st, rt;
  1020.  
  1021.     printf ("dat>\t ...Issuing a Test Unit Ready\n");
  1022.  
  1023.     for ( i = 0; i < 3; i++ ) {
  1024.  
  1025.         errno = 0;
  1026.         if (testunitready00(tpDsreq) == -1) {
  1027.             printf ("dat> Error, RET(dsreq) = %x, errno = %d\n",
  1028.                 RET(tpDsreq), errno);
  1029.             return(-1);
  1030.         }
  1031.  
  1032.         /*   check device's status  */
  1033.         st = STATUS(tpDsreq);
  1034.  
  1035.         switch ( st ) {
  1036.             case STA_GOOD:
  1037.                 return(0);
  1038.  
  1039.             case STA_CHECK:
  1040.                 if ( checkSense() == -1 )
  1041.                     return(-1);
  1042.                 break;
  1043.     
  1044.             default:    
  1045.                 break;
  1046.         }
  1047.  
  1048.         sleep(1);
  1049.     }
  1050.  
  1051.     return(-1);
  1052. }
  1053.     
  1054.         
  1055.  
  1056. /*************************************************************************
  1057.  *            g e t T a p e S i z e         
  1058.  *************************************************************************
  1059.  *
  1060.  *  Name:      getTapeSize
  1061.  *    
  1062.  *  Purpose:   Get tape size and report Max and Remaining K Bytes for
  1063.  *           partition 0. All sizes are in K Bytes.
  1064.  *
  1065.  *  Returns:   0 = Success, -1 = Error
  1066.  *
  1067.  *************************************************************************/
  1068. getTapeSize( int *max_size, int *rem_size )
  1069. {
  1070.  
  1071.     uchar_t        b2, b6, b7, b8;
  1072.     uchar_t        *p;
  1073.  
  1074.     printf ("dat>\t ...Obtainig Tape Capacity values\n");
  1075.  
  1076.     /*
  1077.      *    Issue a Log_Sense, requesting Tape_Capacity page
  1078.      */
  1079.     b2 = 0;
  1080.     b2 |= (PC_BIT | TAPE_CAPACITY_PAGE);
  1081.     b6  = 1;
  1082.     b7  = TAPE_BLOCK_SIZE >> 8;
  1083.     b8  = TAPE_BLOCK_SIZE;
  1084.  
  1085.     fillg1cmd (tpDsreq, CMDBUF(tpDsreq), 
  1086.            LOG_SENSE, 0, b2, 0, 0, 0, b6, b7, b8, 0);
  1087.  
  1088.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_READ | DSRQ_SENSE);      
  1089.     if ( issueCmd() == -1 ) 
  1090.         return(-1);
  1091.  
  1092.     /*
  1093.      *    The Tape Capacity data should be in the tape buffer now.
  1094.      *    Bypass the 4-byte header and access the data.
  1095.      */
  1096.     p = DATABUF(tpDsreq);
  1097.     p += 4;
  1098.  
  1099.     *rem_size = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 
  1100.     *max_size = (p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]; 
  1101.  
  1102.     printf ("dat>\t ...tape size:  Max = %d KBytes, Rem = %d KBytes\n",
  1103.         *max_size, *rem_size );
  1104.  
  1105.     return(0);
  1106. }
  1107.  
  1108.     
  1109.  
  1110. /*************************************************************************
  1111.  *            s w i t c h P a r t           
  1112.  *************************************************************************
  1113.  *
  1114.  *  Name:       switchPart
  1115.  *    
  1116.  *  Purpose:    switches Partition on tape.
  1117.  *
  1118.  *  Returns:    0 = Success, -1 = Error
  1119.  *
  1120.  *************************************************************************/
  1121. switchPart ( part )
  1122. {
  1123.  
  1124.     uchar_t        b1, b2, b4;
  1125.     uchar_t        *p;
  1126.     short       err;
  1127.     ulong        old_dstime;
  1128.  
  1129.     printf ("dat>\t ...Switching to %s Partition, currently in %s Partition\n", 
  1130.         (part == DIR_PARTITION ? "DIR" : "DATA" ),
  1131.         (tpStat.st_part == DIR_PARTITION ? "DIR" : "DATA") );
  1132.  
  1133.  
  1134.     /*
  1135.      *    if we are already in the wanted Partition,
  1136.      *    then simply return.
  1137.      *
  1138.     if ( tpStat.st_part == part ) 
  1139.         return(0);
  1140.     **/
  1141.  
  1142.     /*   
  1143.      *    Do a Mode_Sense and ask for Device_Config page
  1144.          *    ask for no Block Descriptor data
  1145.          */
  1146.     b1  = 0;
  1147.         b1 |= DBD_BIT;
  1148.         b2  = DEVICE_CONFIG_PAGE; 
  1149.     b4  = 100;   /*  size allocated for Dev_Config page  */
  1150.  
  1151.         fillg0cmd (tpDsreq, CMDBUF(tpDsreq), MODE_SENSE, b1, b2, 0, b4, 0);
  1152.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_READ | DSRQ_SENSE);      
  1153.  
  1154.     if ( issueCmd() == -1 ) 
  1155.         return(-1);
  1156.  
  1157.  
  1158.     /*    
  1159.      *   Access Device_Config data. Set CAP bit to 1 and 
  1160.      *   set Active_Partition to requested value
  1161.      */
  1162.     p = DATABUF(tpDsreq);
  1163.  
  1164.     /*  Parameter List Header  */ 
  1165.     p[0] = p[1] = p[2] = p[3] = 0;
  1166.     p[2] |= 0x10;
  1167.  
  1168.     p += 4;       /*  p-> Device Config Data  */
  1169.  
  1170.     p[0]  = DEVICE_CONFIG_PAGE;
  1171.     p[2] |= CAP_BIT;
  1172.     p[3]  = part;
  1173.     p[8]  = 0;
  1174.     p[8] |= RSMK_BIT;
  1175.     p[10] = 0x10;
  1176.     p[4] = p[5] = p[9]  = p[11] = p[12] = p[13] = p[14] = p[15] = 0;
  1177.  
  1178.     /*
  1179.      *    Now issue a Mode_Select with whatever we have in tpBlock
  1180.      */
  1181.     b1  = 0;
  1182.         b1 |= PF_BIT;   /*  Scisi-2 interpretation  */
  1183.         b4  = 20;       /*  4-byte Parameter List + 16-byte Page data */
  1184.  
  1185.         fillg0cmd (tpDsreq, CMDBUF(tpDsreq), MODE_SELECT, b1, 0, 0, b4, 0);
  1186.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_WRITE | DSRQ_SENSE);
  1187.  
  1188.     /*   takes a while   */
  1189.     old_dstime = TIME(tpDsreq);
  1190.     TIME(tpDsreq) = TIMER_SWITCH_PARTITION; 
  1191.  
  1192.     err = issueCmd();
  1193.  
  1194.     TIME(tpDsreq) = old_dstime;  /* put back the timer */
  1195.  
  1196.     if ( err == -1 )  
  1197.         return(-1);
  1198.  
  1199.     tpStat.st_part = part;
  1200.  
  1201.     return (0);
  1202. }
  1203.  
  1204.  
  1205.          
  1206. /*************************************************************************
  1207.  *            g e t T a p e F o r m a t     
  1208.  *************************************************************************
  1209.  *
  1210.  *  Name:       getTapeFormat
  1211.  *    
  1212.  *  Purpose:    figures out whether this tape is Single Partition or 
  1213.  *        Double Partition. Sets the TAPE_DOUBLE_PART bit in 
  1214.  *        tpStat.st_state accordingly.
  1215.  *
  1216.  *  Returns:    0 = Success, -1 = Error
  1217.  *
  1218.  *************************************************************************/
  1219. getTapeFormat( int  *part_size)
  1220. {
  1221.     uchar_t        size_unit[10];
  1222.     uchar_t        b1, b2, b4;
  1223.     uchar_t        *p;
  1224.     uchar_t        psum;
  1225.  
  1226.     printf ("dat>\t ...Figuring out tape format.\n");
  1227.  
  1228.     /*
  1229.      *    Issue a Mode_Sense and ask for Medioum_Partition Page.
  1230.       *    Note that we ask for no Block Descriptor data.
  1231.      */
  1232.     b1 = 0;
  1233.     b1 |= DBD_BIT;
  1234.     b2  = MED_PART_PAGE;
  1235.     b4  = 100;   /*   size allocated  */
  1236.  
  1237.     fillg0cmd (tpDsreq, CMDBUF(tpDsreq), MODE_SENSE, b1, b2, 0, b4, 0);
  1238.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_READ | DSRQ_SENSE);      
  1239.     if ( issueCmd() == -1 ) 
  1240.         return(-1); 
  1241.     /*
  1242.      *    The Medium_Page data should be in tape buffer now.
  1243.      */ 
  1244.     p = DATABUF(tpDsreq);   
  1245.     p += 4;        /*  p-> Medium_Page data  */ 
  1246.     *part_size = 0;
  1247.  
  1248.     /*  if tape is Double_partitioned   */
  1249.     if ( p[3] > 0 ) {
  1250.         psum = p[4] >> 3;
  1251.         *part_size = ( (p[8] << 8) | p[9] );
  1252.  
  1253.         switch ( psum ) {
  1254.             case 0:   sprintf (size_unit, "Bytes");   break;
  1255.             case 1:   sprintf (size_unit, "K Bytes"); break; 
  1256.             case 2:   sprintf (size_unit, "M Bytes"); break;
  1257.         }
  1258.  
  1259.         printf ("dat>\t ...Tape is partitioned, Partition size = %d %s\n",
  1260.              *part_size, size_unit );    
  1261.  
  1262.         tpStat.st_state |= TAPE_DOUBLE_PART;
  1263.         return (0);
  1264.     }
  1265.     else    printf ("dat>\t ...Tape is Single Partition\n"); 
  1266.         
  1267.     tpStat.st_state &= ~TAPE_DOUBLE_PART;
  1268.  
  1269.     return(0);
  1270. }
  1271.  
  1272.  
  1273. /*************************************************************************
  1274.  *            i n i t D o u b l e P a r t 
  1275.  *************************************************************************
  1276.  *
  1277.  *  Name:       initDoublePart
  1278.  *    
  1279.  *  Purpose:    Initializes the tape as Double Partition.
  1280.  *        We set DIR_PART_SIZE Mega bytes for Directory Partition
  1281.  *        (first Partition on tape) and leave the rest of the 
  1282.  *        tape for Files.
  1283.  *
  1284.  *  Returns:    0 = Success, -1 = Error
  1285.  *
  1286.  *************************************************************************/
  1287. initDoublePart()
  1288. {
  1289.  
  1290.         uchar_t         b1, b2, b4;
  1291.     short        err;
  1292.         uchar_t         *p;
  1293.     ulong        old_dstime;
  1294.         int             part_size;
  1295.  
  1296.         printf ("dat>\t ...Initializing the tape as a Double Partition\n");
  1297.  
  1298.  
  1299.         /*
  1300.          *      The tape Medium Partition format is returned to us
  1301.          *      by issuing a Mode_Sense and indication that we want
  1302.          *      Medium _partition Page data. Ask for no Block Descr. data
  1303.          */
  1304.     b1 = 0;
  1305.         b1 |= DBD_BIT;
  1306.         b2  = MED_PART_PAGE;
  1307.     b4  = 100;    /*  size allocated to receive the data  */
  1308.  
  1309.         fillg0cmd (tpDsreq, CMDBUF(tpDsreq), MODE_SENSE, b1, b2, 0, b4, 0);
  1310.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_READ | DSRQ_SENSE);      
  1311.  
  1312.         if ( issueCmd() == -1 )
  1313.                 return(-1);
  1314.  
  1315.  
  1316.     /*      The Medium_Partition data should be available now.
  1317.      *    Set Additional_Partition field and Partition size and set
  1318.       *    IDP bit to 1.
  1319.          */
  1320.         p = DATABUF(tpDsreq);
  1321.     
  1322.     /*   format Parameter List header  */
  1323.     p[0] = p[1] = p[2] = p[3] = 0;
  1324.     p[2] |= 0x10;
  1325.  
  1326.     /*  set Medium_Page data   */
  1327.         p += 4;        /*  p-> Medium_Page data  */
  1328.     p[1] = 8;
  1329.     p[2] = 0;
  1330.     p[4] = 0;
  1331.  
  1332.     p[3] = 1;      /*  Additionla Partition Defined  */
  1333.     p[4] |= (IDP_BIT | PSUM_MBYTES);   
  1334.     p[8]  = 0;
  1335.     p[9]  = DIR_PARTITION_SIZE;
  1336.     
  1337.     b1  = 0;
  1338.     b1 |= PF_BIT;   /*  Scisi-2 interpretation  */
  1339.     b4  = 14;       /*  4-byte Parameter List + 10-byte Page data */    
  1340.  
  1341.     fillg0cmd (tpDsreq, CMDBUF(tpDsreq), MODE_SELECT, b1, 0, 0, b4, 0);
  1342.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_WRITE | DSRQ_SENSE);      
  1343.     /*   takes a long time   */
  1344.     old_dstime = TIME(tpDsreq);
  1345.     TIME(tpDsreq) = TIMER_DOUBLE_PARTIION; 
  1346.     err = issueCmd();
  1347.     TIME(tpDsreq) = old_dstime;  /*  put back old value */
  1348.  
  1349.     if ( err == -1 ) 
  1350.         return(-1);
  1351.  
  1352.         tpStat.st_state |= (TAPE_DOUBLE_PART | TAPE_LOADED);
  1353.     tpStat.st_fno    = 0;
  1354.     tpStat.st_part   = DIR_PARTITION;
  1355.  
  1356.         return(0);
  1357. }
  1358.      
  1359. /*************************************************************************
  1360.  *            w r i t e B l o c k          
  1361.  *************************************************************************
  1362.  *
  1363.  *  Name:       writeBlock
  1364.  *    
  1365.  *  Purpose:    Writes tape's buffer to current position on tape.
  1366.  *
  1367.  *  Returns:    0 = Success, -1 = Error
  1368.  *
  1369.  *************************************************************************/
  1370. writeBlock()
  1371. {
  1372.     uchar_t        b4, b1;
  1373.     short         err;
  1374.     ulong         old_timer;
  1375.     
  1376.     if ( tpStat.st_state & TAPE_WRITE_PROTECT ) {
  1377.         printf ("dat>  Tape is write protected.");
  1378.         return(-1);
  1379.     }
  1380.   
  1381.     b1 = 0x01;   /*  Fixed size blocks  */
  1382.     b4 = 1;      /*  write 1 block  */
  1383.  
  1384.     /**
  1385.     printf ("dat> Writing one block to tape, part = %s\n",
  1386.         (tpStat.st_part == DIR_PARTITION? "DIR":"DATA") );   
  1387.     **/
  1388.  
  1389.     fillg0cmd(tpDsreq, CMDBUF(tpDsreq), WRITE, b1, 0, 0, b4, 0);
  1390.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_WRITE | DSRQ_SENSE);      
  1391.     old_timer = TIME(tpDsreq);
  1392.     TIME(tpDsreq) = TIMER_READ_WRITE;
  1393.     err = issueCmd();
  1394.     TIME(tpDsreq) = old_timer;
  1395.     
  1396.     if ( err == 0 ) 
  1397.         tpStat.st_state |= TAPE_WRITTEN;
  1398.  
  1399.     return ( err );
  1400. }
  1401.  
  1402. /*************************************************************************
  1403.  *                       r e a d B l o c k
  1404.  *************************************************************************
  1405.  *
  1406.  *  Name:       readBlock
  1407.  *
  1408.  *  Purpose:    Reads tpBlock buffer from current position on tape.
  1409.  *
  1410.  *  Returns:    0 = Success, -1 = Error
  1411.  *
  1412.  *************************************************************************/
  1413. readBlock()
  1414. {
  1415.         uchar_t         b4, b1;
  1416.     ulong        old_timer;
  1417.         int             err;
  1418.  
  1419.     b1 = 0x01;   /*  Fixed size blocks  */
  1420.         b4 = 1;      /*  read 1 block  */
  1421.  
  1422.         /**
  1423.         printf ("dat>\t ...Reading one block from tape, part = %s\n",
  1424.                 (tpStat.st_part == DIR_PARTITION? "DIR":"DATA") );
  1425.         **/
  1426.  
  1427.         fillg0cmd(tpDsreq, CMDBUF(tpDsreq), READ, b1, 0, 0, b4, 0);
  1428.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_READ | DSRQ_SENSE); 
  1429.  
  1430.         old_timer = TIME(tpDsreq);
  1431.         TIME(tpDsreq) = TIMER_READ_WRITE;
  1432.         err = issueCmd();
  1433.         TIME(tpDsreq) = old_timer;
  1434.  
  1435.         return ( err ); 
  1436.  
  1437. }
  1438.  
  1439. /*************************************************************************
  1440.  *                      w r i t e F m      
  1441.  *************************************************************************
  1442.  *
  1443.  *  Name:       writeFm
  1444.  *
  1445.  *  Purpose:    Writes File Mark at current position. 
  1446.  *
  1447.  *  Returns:    0 = Success, -1 = Error
  1448.  *
  1449.  *************************************************************************/
  1450. writeFm()
  1451. {
  1452.         uchar_t         b4;     
  1453.         int             err;    
  1454.  
  1455.         if ( tpStat.st_state & TAPE_WRITE_PROTECT ) {
  1456.                 printf ("dat>  Tape is write protected.");
  1457.                 return(-1);
  1458.         }
  1459.  
  1460.         b4 = 0x01;   /*   we always write one File Mark  */
  1461.  
  1462.     printf ("dat>\t ...Writing End_of_file_mark\n");
  1463.         fillg0cmd(tpDsreq, CMDBUF(tpDsreq), WRITE_FM, 0, 0, 0, b4, 0);   
  1464.         filldsreq (tpDsreq, 0, 0, DSRQ_WRITE | DSRQ_SENSE); 
  1465.  
  1466.         err = issueCmd();
  1467.  
  1468.     tpStat.st_fno++;
  1469.         tpStat.st_state |= TAPE_WRITTEN;
  1470.  
  1471.         return ( err );
  1472. }
  1473.  
  1474. /*************************************************************************
  1475.  *            r e w i n d T a p e          
  1476.  *************************************************************************
  1477.  *
  1478.  *  Name:       rewindTape
  1479.  *    
  1480.  *  Purpose:    Rewind the tape.
  1481.  *
  1482.  *  Returns:    0 = Success, -1 = Error    
  1483.  *
  1484.  *************************************************************************/
  1485. rewindTape()
  1486. {
  1487.  
  1488.     register int  err;
  1489.     ulong          old_timer;
  1490.  
  1491.     printf ("dat>\t ...Rewinding the tape.\n");
  1492.  
  1493.     /*   Create a Rewind Cdb buffer and issue the command  */ 
  1494.  
  1495.     fillg0cmd (tpDsreq, CMDBUF(tpDsreq), REWIND, 0, 0 , 0, 0, 0 );  
  1496.         filldsreq (tpDsreq, 0, 0, DSRQ_WRITE | DSRQ_SENSE); 
  1497.  
  1498.         old_timer = TIME(tpDsreq);
  1499.         TIME(tpDsreq) = TIMER_REWIND;
  1500.         err = issueCmd();
  1501.         TIME(tpDsreq) = old_timer;
  1502.  
  1503.     tpStat.st_fno = 0;
  1504.  
  1505.     return ( err ); 
  1506. }
  1507.  
  1508.  
  1509. /*************************************************************************
  1510.  *                      l o a d T a p e
  1511.  *************************************************************************
  1512.  *
  1513.  *  Name:       loadTape
  1514.  *
  1515.  *  Purpose:     Loads the tape and changes tape block size to our
  1516.  *         own 1024 byte size (default is 512).
  1517.  *
  1518.  *  Returns:     0 = Success, -1 = Failure
  1519.  *
  1520.  *************************************************************************/
  1521. loadTape()
  1522. {
  1523.     uchar_t        b1, b4;
  1524.     uchar_t        *p;
  1525.  
  1526.     printf ("dat>\t ...Loading the tape.\n");
  1527.  
  1528.     fillg0cmd (tpDsreq, CMDBUF(tpDsreq), LOAD_UNLOAD, 0, 0, 0, LOAD_BIT, 0);
  1529.         filldsreq (tpDsreq, 0, 0, DSRQ_READ | DSRQ_SENSE); 
  1530.     
  1531.     if ( issueCmd() == -1 ) 
  1532.         return (-1);
  1533.  
  1534.     /*
  1535.      *    Issue a Mode_Select and set the tape block size to ours.
  1536.      */
  1537.  
  1538.     /*   set Parameter list for Mode_Select   */
  1539.     p = tpBlock;
  1540.     p[0] = p[1] = p[2] = 0;
  1541.     p[2] |= 0x10;
  1542.     p[3] =  8;
  1543.  
  1544.     /*   set Block Descriptor fields   */
  1545.     p += 4;
  1546.     p[0] = p[1] = p[2] = p[3] = p[4] = 0;
  1547.     p[5] = TAPE_BLOCK_SIZE >> 16;   /*  MSB of block size  */
  1548.     p[6] = TAPE_BLOCK_SIZE >>  8;   /*    block size       */
  1549.     p[7] = TAPE_BLOCK_SIZE;         /*  LSB of block size  */
  1550.  
  1551.     b1 = 0;
  1552.     b1 |= PF_BIT;   /*  Scsi-2 format  */
  1553.     b4  = 12;       /*  4 for Param. List header, 8 for Block Descriptor */
  1554.  
  1555.     fillg0cmd (tpDsreq, CMDBUF(tpDsreq), MODE_SELECT, b1, 0, 0, b4, 0);
  1556.         filldsreq (tpDsreq, tpBlock, TAPE_BLOCK_SIZE, DSRQ_WRITE | DSRQ_SENSE); 
  1557.  
  1558.     if ( issueCmd() == -1 ) 
  1559.         return (-1);
  1560.  
  1561.     tpStat.st_state |= TAPE_LOADED;
  1562.     tpStat.st_state &= ~TAPE_WRITTEN;
  1563.     tpStat.st_part   = DIR_PARTITION;
  1564.  
  1565.     return(0);
  1566. }
  1567.  
  1568. /*************************************************************************
  1569.  *                      u n l o a d T a p e 
  1570.  *************************************************************************
  1571.  *
  1572.  *  Name:        unloadTape
  1573.  *
  1574.  *  Purpose:     Unloads the tape.
  1575.  *
  1576.  *  Returns:     0 = Success, -1 = Failure
  1577.  *
  1578.  *************************************************************************/
  1579. unloadTape()
  1580. {
  1581.  
  1582.     ulong    old_timer;
  1583.     short    err;
  1584.  
  1585.     printf ("dat>\t ...Unloading the tape\n");
  1586.  
  1587.         fillg0cmd (tpDsreq, CMDBUF(tpDsreq), LOAD_UNLOAD, 0, 0, 0, 
  1588.            UNLOAD_BIT, 0);
  1589.  
  1590.         filldsreq (tpDsreq, 0, 0, DSRQ_READ | DSRQ_SENSE); 
  1591.  
  1592.         old_timer = TIME(tpDsreq);
  1593.         TIME(tpDsreq) = TIMER_READ_WRITE;
  1594.         err = issueCmd();
  1595.         TIME(tpDsreq) = old_timer;
  1596.  
  1597.         return ( err ) ; 
  1598. }
  1599.  
  1600.         
  1601. /*************************************************************************
  1602.  *            i s s u e C m d             
  1603.  *************************************************************************
  1604.  *
  1605.  *  Name:       issueCmd
  1606.  *    
  1607.  *  Purpose:    Issues a previously set up (in tpDsreq) command
  1608.  *        Retries for Busy condition and so on.
  1609.  *
  1610.  *  Returns:    0 = Successful, -1 = Error
  1611.  *
  1612.  *************************************************************************/
  1613. issueCmd()
  1614. {
  1615.     
  1616.     register int    fd, issue_more, err;
  1617.     uchar_t        stat, ret;
  1618.  
  1619.     issue_more = MAX_BUSY_RETRY;
  1620.     fd = getfd(tpDsreq);
  1621.     
  1622.     while ( issue_more ) {
  1623.  
  1624.         errno = 0;
  1625.         if ( doscsireq ( fd, tpDsreq ) == -1 ) { 
  1626.                printf ("dat> Error ..RET(dsreq) = %x, errno = %d\n",
  1627.                     RET(tpDsreq), errno );
  1628.                 return (-1);
  1629.         }
  1630.  
  1631.         /*   check Target's returned status   */
  1632.         stat = STATUS(tpDsreq);
  1633.     /* printf ("dat> STATUS(dsreq) = %x, RET(dsreq) = %x\n", stat, ret); */
  1634.  
  1635.         /*   see what happened  */
  1636.         switch ( stat ) {
  1637.  
  1638.             /*  good   */
  1639.             case STA_GOOD:
  1640.                 return (0);
  1641.  
  1642.             /*   darn thing is busy - try again  */
  1643.             case STA_BUSY:
  1644.                 printf ("dat>\t ...Tape busy - trying again...\n");
  1645.                 issue_more--;
  1646.                 sleep(1);
  1647.                 break;
  1648.  
  1649.             /*   something went wrong   */
  1650.             case STA_CHECK:
  1651.                 if ( checkSense() == 0 ) {
  1652.                                      return(0);
  1653.                 }
  1654.                 return (-1);
  1655.         }
  1656.     }
  1657.  
  1658.      return(-1);
  1659. }
  1660.  
  1661.  
  1662.  
  1663. /*************************************************************************
  1664.  *            c h e c k S e n s e          
  1665.  *************************************************************************
  1666.  *
  1667.  *  Name:        checkSense
  1668.  *    
  1669.  *  Purpose:     A Unit Check has occured. We check the sense byte 12,13
  1670.  *         and try to come up with a good error description
  1671.  *
  1672.  *  Returns:     0 = Try again, else 1 for error.
  1673.  *
  1674.  *************************************************************************/
  1675. checkSense()
  1676. {
  1677.     uchar_t        senseKey;
  1678.     uchar_t         b15;    
  1679.     ushort_t    b1213;
  1680.     caddr_t        sbuf;
  1681.  
  1682.     sbuf = SENSEBUF(tpDsreq);
  1683.  
  1684.     b15 = sbuf[15];
  1685.     senseKey  = sbuf[2] & 0x0F; 
  1686.  
  1687.     b1213 = (sbuf[12] << 8)    | (sbuf[13]);
  1688.  
  1689.     printf ("dat>\t ...Sense key: %x,  b12,13 = %x, %x\n",
  1690.         senseKey, sbuf[12], sbuf[13] );
  1691.  
  1692.     /*   if no Sense Key, then everything is ok  */
  1693.     if (senseKey == 0 ) {
  1694.         if ( b1213 == 0x0001) 
  1695.             printf ("dat>\t ...SENSE: File_Mark Detected\n");
  1696.         if ( b1213 == 0x0002 )
  1697.             printf ("dat>\t ...ERROR: End of Partition\n");
  1698.         if ( b1213 == 0x0003) 
  1699.                         printf ("dat>\t ...ERROR: Setmark Detected\n");
  1700.         if ( b1213 == 0x0004)
  1701.             printf ("dat>\t ...ERROR: Beginning of Partition\n");
  1702.  
  1703.         return(-1);
  1704.     }
  1705.  
  1706.     /*   Write protected tape   */
  1707.     if ( senseKey == 0x07 ) {
  1708.         printf ("dat>\t ...SENSE: Tape is Write Protected\n");
  1709.         tpStat.st_state |= TAPE_WRITE_PROTECT;
  1710.         return(0);  
  1711.     }
  1712.  
  1713.     /*   In process of becoming ready  */
  1714.     if ( (senseKey == 0x02) && (b1213 == 0x0401) ) {
  1715.                 printf ("dat>\t ...SENSE: Tape is becoming Ready\n");
  1716.         return(0); 
  1717.     }
  1718.  
  1719.  
  1720.     /*   Not ready to ready transition in progress */
  1721.     if ( (senseKey == 0x06) && (b1213 == 0x2800) ) {
  1722.                 printf ("dat>\t ...SENSE: Not_Reay to Ready transition\n");
  1723.         return(0);
  1724.     }
  1725.  
  1726.     /*    Write Protect tape  */
  1727.     if ( (senseKey == 0x07) && (b1213 == 0x2700) ) {
  1728.                 printf ("dat>\t ...SENSE: Tape is Write Protected\n");
  1729.         tpStat.st_state |= TAPE_WRITE_PROTECT;
  1730.         return(0);
  1731.     }
  1732.  
  1733.     /*    End of Tape or Partition   */
  1734.     if ( (senseKey == 0x0D) && (b1213 = 0x0002) ) {
  1735.                 printf ("dat>\t ...SENSE: End of Tape or Partition\n");
  1736.         return(-1);
  1737.     }
  1738.  
  1739.     /*   No Cassette is present     */
  1740.     if ( (senseKey == 0x02) && (b1213 == 0x3a00) ) {
  1741.                 printf ("dat>\t ...SENSE: No Cassette in the drive\n");
  1742.         return(-1);
  1743.     }
  1744.  
  1745.     return(-1);
  1746. }
  1747.  
  1748.  
  1749.  
  1750. /*************************************************************************
  1751.  *             s h o w H e l p               
  1752.  *************************************************************************
  1753.  *
  1754.  *  Name:       showHelp
  1755.  *    
  1756.  *  Purpose:    Show supported commands.
  1757.  *
  1758.  *  Returns:    None.
  1759.  *
  1760.  *************************************************************************/
  1761. showHelp()
  1762. {
  1763.     printf (" Available commands are: \n\n");
  1764.     printf (" init:   Initialize the tape\n");
  1765.     printf (" open:   Open the tape\n");
  1766.     printf (" dir:    Show directory of the Tape\n");
  1767.     printf (" diru:   Directory Up\n");
  1768.     printf (" dird:   Directory Down\n");
  1769.     printf (" read:   Read a file from Tape to Disk\n");             
  1770.     printf (" write:  Write a file from Disk to Tape\n");
  1771.     printf (" close:  Close the tape\n");
  1772.     printf (" exit:   Exit the program\n");
  1773. }
  1774.  
  1775.  
  1776. /*************************************************************************
  1777.  *            s h o w D i r               
  1778.  *************************************************************************
  1779.  *
  1780.  *  Name:      showDir
  1781.  *    
  1782.  *  Purpose:   Shows the Directory of the Tape
  1783.  *
  1784.  *  Returns:   None
  1785.  *
  1786.  *************************************************************************/
  1787. showDir( int sno )
  1788. {
  1789.  
  1790.     char        *prepDate();
  1791.     register int    i, row;
  1792.     tpDir_t        *tpd;
  1793.  
  1794.     if ( !(tpStat.st_state & TAPE_LOADED) ) { 
  1795.         printf ("dat> No tape is opened yet\n");
  1796.         return;
  1797.     }
  1798.  
  1799.     if ( tpCb.cb_dtot == 0 ) {
  1800.         printf ("dat> Tape is empty\n");    
  1801.         return;
  1802.     }
  1803.  
  1804.     /*    adjust starting file number  */
  1805.     if ( sno >= tpCb.cb_dtot) 
  1806.         sno -= SCREEN_SIZE;
  1807.  
  1808.     if ( sno < 0 ) 
  1809.         sno = 0;
  1810.  
  1811.     /*   there is something to show    */
  1812.     printf ("%-3s  %-20s  %-6s  %-25s  %-5s\n",
  1813.         "No",  "Name", "Length", "Date/Time", "Type");
  1814.  
  1815.     for ( row = 0; row < SCREEN_SIZE; row++, sno++ ) {
  1816.  
  1817.         if ( sno >= tpCb.cb_dtot )
  1818.             break;  
  1819.  
  1820.         tpd = &tpDir[sno];
  1821.             printf ("%-3d  %-20s  %-6d  %-25s  %-5s\n",
  1822.             tpd->d_fno, 
  1823.             tpd->d_name,
  1824.             tpd->d_len,
  1825.             prepDate(&tpd->d_time), 
  1826.             (tpd->d_type == TYPE_FILE ? "File":"Unknown") );
  1827.     }
  1828.  
  1829.     printf ("\nTotal files: %d         Current position: %d\n\n",
  1830.         tpCb.cb_dtot, tpStat.st_fno );
  1831.     tpStat.st_dno = sno;
  1832.  
  1833. }
  1834.  
  1835.  
  1836. /*************************************************************************
  1837.  *            p r e p D a t e               
  1838.  *************************************************************************
  1839.  *
  1840.  *  Name:       prepDate 
  1841.  *    
  1842.  *  Purpose:    formats Date/Time (returns whatever ctime() returns 
  1843.  *        minues the CR at the end).
  1844.  *
  1845.  *  Returns:    Pointer to the dat format
  1846.  *
  1847.  *************************************************************************/
  1848. char *
  1849. prepDate( long *d)
  1850. {
  1851.     register int    i;
  1852.     register char   *s;
  1853.  
  1854.     s = ctime (d);
  1855.     for ( i = 0; s[i] != '\n'; i++ );
  1856.     s[i] = '\0';
  1857.     
  1858.     return (s);
  1859. }
  1860.  
  1861.          
  1862.  
  1863.  
  1864. /*************************************************************************
  1865.  *            c l e a n U p               
  1866.  *************************************************************************
  1867.  *
  1868.  *  Name:       cleanUp
  1869.  *    
  1870.  *  Purpose:    Unloads the tape if necessary and exits the program
  1871.  *
  1872.  *  Returns:    None
  1873.  *
  1874.  *************************************************************************/
  1875. cleanUp()
  1876. {
  1877.  
  1878.     if ( tpStat.st_state & TAPE_LOADED ) 
  1879.         unloadTape();
  1880.  
  1881.     dsclose(tpDsreq);
  1882.     printf ("dat>  Dsreq connection closed\n");
  1883.     exit(0);
  1884. }
  1885.  
  1886.